package org.az.netty_study.p03_nio组件之selector._4_多线程selector;

import lombok.extern.slf4j.Slf4j;
import org.az.netty_study.ByteBufferUtil;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

@Slf4j
public class Worker implements Runnable{

    private Thread thread;
    private Selector selector;
    private String name;
    private boolean started;

    public Worker(String name) {
        this.name = name;
    }

    //初始化线程和selector
    public void init() throws IOException {
        //这个方法执行一次
        if (!started) {
            selector = Selector.open();
            thread = new Thread(this, name);
            thread.start();
            started = true;
        }
    }

    //给当前selector注册SocketChannel
    public void register(SocketChannel sc, int key, Object attach) throws ClosedChannelException {
        //唤醒selector，本质是给selector发一个凭证，将来selector.select时发现有一个凭证，就消耗掉，然后本次就不阻塞了
        selector.wakeup();
        sc.register(selector, key, attach);
    }
    @Override
    public void run() {
        while (true) {
            //worker只关注读写事件
            try {
                //这个方法也会阻塞sc.register方法
                int select = selector.select();
                Set<SelectionKey> keys1 = selector.keys();
                //如果select = 0，则说明selector.select被解除阻塞不是因为发生了事件，而是selector被唤醒了
                if (select > 0) {
                    Set<SelectionKey> keys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = keys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        System.out.println(key);
                        iterator.remove();
                        //这里为了简单，就不演示异常关闭的处理，一次读不完的情况，一次写不完的情况
                        if (key.isReadable()) {
                            SocketChannel sc = (SocketChannel)key.channel();
                            ByteBuffer buffer = ByteBuffer.allocate(16);
                            int read = sc.read(buffer);
                            if (read == -1) {
                                key.channel();
                                sc.close();
                                log.info("客户端{}关闭了连接", sc);
                            }else {
                                buffer.flip();
                                log.info("获取到客户端{}发送的信息...", sc);
                                ByteBufferUtil.debugAll(buffer);
                            }
                        } else if (key.isWritable()) {
                            //处理可写事件...
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
